package edu.xlh.travel.service.impl;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import edu.xlh.travel.aipay.util.AlipayConfig;
import edu.xlh.travel.asynctask.AsyncTask;
import edu.xlh.travel.common.Result;
import edu.xlh.travel.dao.EmailDao;
import edu.xlh.travel.dao.ForeHotelOrderDao;
import edu.xlh.travel.dao.HotelDao;
import edu.xlh.travel.dao.TotalScoreDao;
import edu.xlh.travel.enumeration.EmailTypeEnum;
import edu.xlh.travel.enumeration.OrderStatusEnum;
import edu.xlh.travel.enumeration.OrderTypeEnum;
import edu.xlh.travel.pojo.*;
import edu.xlh.travel.pojo.querypojo.QueryOrders;
import edu.xlh.travel.service.ForeHotelOrderService;
import edu.xlh.travel.util.BigDecimalUtil;
import edu.xlh.travel.util.DateTimeUtil;
import edu.xlh.travel.util.IdWorker;
import edu.xlh.travel.util.PageResult;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpSession;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Transactional
public class ForeHotelOrderServiceImpl implements ForeHotelOrderService {
    @Autowired
    private ForeHotelOrderDao foreHotelOrderDao;
    @Autowired
    private HotelDao hotelDao;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private AsyncTask asyncTask;
    @Autowired
    private TotalScoreDao totalScoreDao;
    @Autowired
    private EmailDao emailDao;

    private static final String orderTypePrefix= OrderTypeEnum.hotelOrder.getCode();

    @Override
    public Result buy(HashMap<String, Object> hotelHashMap, HttpSession session) {
        User user = (User)session.getAttribute("user");
        if (user == null) {
            return new Result(201);
        }
        //1.考虑入住时间
        String begin_time = (String)hotelHashMap.get("begin");
        String end_time = (String)hotelHashMap.get("end");
        Date begin_timeToDate = DateTimeUtil.strToDate(begin_time, "yyyy-MM-dd");
        Date end_timeToDate = DateTimeUtil.strToDate(end_time, "yyyy-MM-dd");
        //计算相差天数
        long days = (end_timeToDate.getTime() - begin_timeToDate.getTime()) / (24*3600*1000);
        //计算总金额
        //String price = (String)hotelHashMap.get("price");
        String qty = (String)hotelHashMap.get("qty");
        String hotelId = (String) hotelHashMap.get("id");
        Hotel hotel = hotelDao.findById(hotelId).get();
        Integer stock = hotel.getStock();
        if (stock < Integer.parseInt(qty)) {
            return new Result(202);
        }/*else {
            Integer newStock = stock - Integer.parseInt(qty);
            hotel.setStock(newStock);
            hotelDao.save(hotel);
        }*/
        //n天*price
        double nDayPrice = hotel.getPrice() * days;
        //n张票
        BigDecimal payment = BigDecimalUtil.mul(nDayPrice,Integer.parseInt(qty));
        //获取当前时间
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = df.format(new Date());// new Date()为获取当前系统时间，也可使用当前时间戳
        //订单表字段设置
        Hotel_orders hotel_orders = new Hotel_orders();
        String userId = user.getId();
        hotel_orders.setId(idWorker.nextId()+"" );
        hotel_orders.setUserId(userId);
        hotel_orders.setUserName(user.getName());
        hotel_orders.setPhone(user.getMobile());
        hotel_orders.setScenicId(hotelId);
        hotel_orders.setScenicName(hotel.getAddr());
        hotel_orders.setPayment(payment);
        hotel_orders.setQty(Integer.parseInt(qty));
        hotel_orders.setStatus("0");
        hotel_orders.setStartTime(begin_time);
        hotel_orders.setEndTime(end_time);
        hotel_orders.setPaytime(null);
        hotel_orders.setDelFlag("0");
        hotel_orders.setCreateTime(DateTimeUtil.strToDate(date,"yyyy-MM-dd HH:mm:ss"));
        hotel_orders.setModifyTime(DateTimeUtil.strToDate(date,"yyyy-MM-dd HH:mm:ss"));
        foreHotelOrderDao.save(hotel_orders);

        //邮件
        Email email = new Email()
                .setId(idWorker.nextId() + "")
                .setOrderId(hotel_orders.getId())
                .setSendOutUserId("")
                .setSendOutName("系统回复")
                .setReceiveUserId(hotel_orders.getUserId())
                .setReceiveName(hotel_orders.getUserName())
                .setSendTime(new Date())
                .setIsRead("0")
                .setDelFlag("0")
                .setEmailType(EmailTypeEnum.order.getCode())
                .setOrderType(OrderTypeEnum.hotelOrder.getCode());
        emailDao.save(email);

        System.out.println("主线程id: "+Thread.currentThread().getId());

        //共用
        //设置已下单订单未支付期限
        //1.设置订单失效时间
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MINUTE , 4);
        //int minute4later =  (int)(System.currentTimeMillis()/1000/60%60);
        System.out.println("四分钟后的时间"+cal.get(Calendar.MINUTE));
        //2.利用zset的score升序特性，将过期时间minute4later作为score
        stringRedisTemplate.opsForZSet().add("orderId" , hotel_orders.getId() ,cal.get(Calendar.MINUTE));
        //System.out.println(System.currentTimeMillis()/ 1000);
        //3.异步处理失效订
        asyncTask.doHandleExpiredOrder(OrderTypeEnum.hotelOrder.getCode());
        return  new Result(200);
    }

    @Override
    public Result findAllOrders(HttpSession session) {
        User user = (User)session.getAttribute("user");
        List<Hotel_orders> ordersList = foreHotelOrderDao.findByUserIdOrderByModifyTimeDesc(user.getId());
        return new Result(0 , ordersList);
    }

    @Override
    public Result cancelOrder(String orderId, HttpSession session) {
        User user = (User)session.getAttribute("user");
        if (user == null) {
            return new Result(201,"用户登录信息过期，请重新登录");
        }
        //获取当前时间
       /* SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = df.format(new Date());// new Date()为获取当前系统时间，也可使用当前时间戳*/
        Hotel_orders hotel_orders = new Hotel_orders().setId(orderId)
                .setModifyTime(new Date())
                .setStatus(OrderStatusEnum.endTrade.getCode());
        try{
            foreHotelOrderDao.updateHotel_ordersById(hotel_orders);
        }catch(Exception e){
            return new Result(202,"订单取消失败");
        }
        return new Result(200,"订单取消成功");
    }

    @Override
    public String toPayOrderPage(String orderId, HttpSession session, Model model) {
        Hotel_orders hotel_orders = foreHotelOrderDao.findById(orderId).get();
        model.addAttribute("hotel_orders" , hotel_orders);
        return "/user/payHotelOrderPage";
    }

    @Override
    public String doPayForOrder(HashMap<String, String> orderInfo, HttpSession session) throws AlipayApiException {
        String orderId = (String)orderInfo.get("orderId");
        String payment = (String)orderInfo.get("payment");
        String payType = (String)orderInfo.get("payType");
        System.out.println("订单号："+orderId+"....."+"总金额："+payment);
        //发起支付
        Hotel_orders hotel_orders = foreHotelOrderDao.findById(orderId).get();
        //保存留言
        String remarks = orderInfo.get("remarks");
        foreHotelOrderDao.saveRemarks(orderId, remarks);
        String scenicId = hotel_orders.getScenicId();
        Hotel hotel = hotelDao.findById(scenicId).get();
        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

        //商户订单号，商户网站订单系统中唯一订单号，必填
        String out_trade_no = orderTypePrefix + orderId;
        //付款金额，必填
        User user = (User) session.getAttribute("user");
        String userId = user.getId();
        String isVip = user.getIsVip();
        if ("1".equals(isVip)) {
            if ("2".equals(payType)) {
                TotalScore byUserId = totalScoreDao.findByUserId(userId);
                int totalScore = Integer.parseInt(byUserId.getTotalScore());
                if (totalScore >= 10)
                    payment = String.valueOf(Double.valueOf(payment) - 10.00);
                totalScore -= 10;
                totalScoreDao.updateTotalScoreByUserIdForPay(userId , String.valueOf(totalScore));
            }
        }
        String total_amount = payment;
        //订单名称，必填
        String subject = hotel.getName();
        //商品描述，可空
        String body = "用户订购商品个数：" + hotel_orders.getQty();

        // 该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天（1c-当天的情况下，无论交易何时创建，都在0点关闭）。 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
        String timeout_express = "30m";

        alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
                + "\"total_amount\":\""+ total_amount +"\","
                + "\"subject\":\""+ subject +"\","
                + "\"body\":\""+ body +"\","
                + "\"timeout_express\":\""+ timeout_express +"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        //请求
        String result = alipayClient.pageExecute(alipayRequest).getBody();
        return result;
    }

    @Override
    public void updateHotelOrderByEntitiy(Hotel_orders hotel_orders) {
        foreHotelOrderDao.updateHotelOrderByEntitiy(hotel_orders);
    }

    @Override
    public Hotel_orders findById(String orderId) {
        return foreHotelOrderDao.findById(orderId).get();
    }

    @Override
    public void updateStatusByHotelOrderId(String status, String orderId) {
        foreHotelOrderDao.updateStatusByHotelOrderId(status , orderId);
    }


    @Override
    public void updateStockByHotelOrderId(String orderId, int stocks) {
        foreHotelOrderDao.updateStockByHotelOrderId(orderId , stocks);
    }

    @Override
    public int countByUserId(String userId) {
        return foreHotelOrderDao.countByUserId(userId);
    }

    @Override
    public Result findAll(int page, int limit, HttpSession session) {
        User user = (User)session.getAttribute("admin");
        if (user == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        Specification specification = new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> orderList = new ArrayList<>();
                orderList.add(criteriaBuilder.equal(root.get("delFlag"), "0"));
                return criteriaBuilder.and(orderList.toArray(new Predicate[orderList.size()]));
            }
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "modifyTime");
        PageRequest pageRequest =  PageRequest.of(page-1, limit , sort);
        Page<Hotel_orders> ordersPageList = foreHotelOrderDao.findAll(specification , pageRequest);
        return new Result(0 , "分页数据获取成功" , new PageResult<Hotel_orders>(ordersPageList.getTotalElements(), ordersPageList.getContent()));
    }

    @Override
    public Result QueryHotelOrdersByCondition(Integer page, Integer limit, QueryOrders queryOrders, HttpSession session) {
        User userFromSession = (User)session.getAttribute("admin");
        if (userFromSession == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        String paytime = DateTimeUtil.dateToStr(queryOrders.getPaytime(), "yyyy-MM-dd");
        String paytimeStartTimeToStr = paytime + " " + "00:00:00";
        String paytimeEndTimeToStr = paytime + " " + "23:59:59";
        Date paytimeStartTimeToDate = DateTimeUtil.strToDate(paytimeStartTimeToStr , "yyyy-MM-dd HH:mm:ss");
        Date paytimeEndTimeToDate = DateTimeUtil.strToDate(paytimeEndTimeToStr , "yyyy-MM-dd HH:mm:ss");

        String crateTime = DateTimeUtil.dateToStr(queryOrders.getCreateTime(), "yyyy-MM-dd");
        String crateTimeStartTimeToStr = crateTime + " " + "00:00:00";
        String crateTimeEndTimeToStr = crateTime + " " + "23:59:59";
        Date crateTimeStartTimeToDate = DateTimeUtil.strToDate(crateTimeStartTimeToStr , "yyyy-MM-dd HH:mm:ss");
        Date crateTimeEndTimeToDate = DateTimeUtil.strToDate(crateTimeEndTimeToStr , "yyyy-MM-dd HH:mm:ss");

        Specification specification = new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> hotelOrdersList = new ArrayList<>();
                if (StringUtils.isNotEmpty(queryOrders.getId())) {
                    hotelOrdersList.add(criteriaBuilder.equal(root.get("id") , queryOrders.getId()));
                }
                if (StringUtils.isNotEmpty(queryOrders.getStatus())) {
                    hotelOrdersList.add(criteriaBuilder.like(root.get("status") , "%" + queryOrders.getStatus() + "%"));
                }
                if (queryOrders.getCreateTime() != null) {
                    hotelOrdersList.add(criteriaBuilder.between(root.get("createTime") ,  crateTimeStartTimeToDate , crateTimeEndTimeToDate));
                }
                if (queryOrders.getPaytime() != null) {
                    hotelOrdersList.add(criteriaBuilder.between(root.get("paytime") ,  paytimeStartTimeToDate , paytimeEndTimeToDate));
                }
                hotelOrdersList.add(criteriaBuilder.equal(root.get("delFlag") , "0"));
                return criteriaBuilder.and(hotelOrdersList.toArray(new Predicate[hotelOrdersList.size()]));
            }
        };
        Sort sort = Sort.by(Sort.Direction.DESC, "modifyTime");
        Pageable pageable= PageRequest.of(page-1,limit,sort);
        Page<Hotel_orders>  hotelOrdersListPage  = foreHotelOrderDao.findAll(specification , pageable);
        if (hotelOrdersListPage!=null) {
            return new Result(0 , "酒店订单分页数据获取成功" , new PageResult<Hotel_orders>(hotelOrdersListPage.getTotalElements(), hotelOrdersListPage.getContent()));
        }
        return new Result(201 , "酒店订单列表失败");
    }


    @Override
    public Result arrangeAccommodation(String id, @RequestBody Hotel_orders hotel_orders , HttpSession session) {
        User userFromSession = (User)session.getAttribute("admin");
        if (userFromSession == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        SimpleDateFormat dateFormat=new SimpleDateFormat("YYYY-MM-dd");
        int compareToStart = dateFormat.format(new Date()).compareTo(hotel_orders.getEndTime().substring(0, 10));
        if (compareToStart > 0) {
            return new Result(203 , "已错过最迟安排时间段，不可安排");
        }
        try{
            foreHotelOrderDao.updateHotelOrderModifyTimeById(id , OrderStatusEnum.arrangeing.getCode() , new Date());
            Email email = new Email()
                    .setId(idWorker.nextId() + "")
                    .setOrderId(id)
                    .setSendOutUserId(userFromSession.getId())
                    .setSendOutName(userFromSession.getName())
                    .setReceiveUserId(hotel_orders.getUserId())
                    .setReceiveName(hotel_orders.getUserName())
                    .setSendTime(new Date())
                    .setIsRead("0")
                    .setDelFlag("0")
                    .setEmailType(EmailTypeEnum.arrange.getCode())
                    .setOrderType(OrderTypeEnum.hotelOrder.getCode());
            emailDao.save(email);
        }catch (Exception e) {
            return new Result(202 , "安排失败");
        }
        return new Result(200 , "安排成功");
    }

    @Override
    public Result determine(String orderId, HttpSession session) {
        try{
            Hotel_orders hotel_orders = foreHotelOrderDao.findById(orderId).get();
            if (!OrderStatusEnum.arrangeing.getCode().equals(hotel_orders.getStatus())){
                return new Result(204 , "订单状态不可入住");
            }
            SimpleDateFormat dateFormat=new SimpleDateFormat("YYYY-MM-dd");
            int compareToStart = dateFormat.format(new Date()).compareTo(hotel_orders.getStartTime().substring(0, 10));
            if (compareToStart < 0) {
                return new Result(205 , "未到入住规定起始时间，暂时无法入住");
            }
            int compareEnd = dateFormat.format(new Date()).compareTo(hotel_orders.getEndTime().substring(0 , 10));
            if (compareEnd > 0) {
                return new Result(203 , "不好意思，已超过入住时间段");
            }
            Hotel hotel = hotelDao.findById(hotel_orders.getScenicId()).get();
            foreHotelOrderDao.updateStockByHotelOrderId(orderId , hotel.getStock() - hotel_orders.getQty());
            foreHotelOrderDao.updateHotelOrderModifyTimeById(orderId , OrderStatusEnum.staying.getCode() , new Date());
        }catch (Exception e) {
            return new Result(202 , "入住失败");
        }
        return new Result(200 , "入住成功");
    }

    @Override
    public Result findByOrderId(String orderId, String emailId , HttpSession session) {
        emailDao.updateIsReadById(emailId , "1");
        Hotel_orders hotel_orders = foreHotelOrderDao.findById(orderId).get();
        HashMap  hashMap  = new HashMap();
        Hotel hotel = hotelDao.findById(hotel_orders.getScenicId()).get();
        hashMap.put("order" , hotel_orders);
        hashMap.put("hotel" , hotel);
        String status = hotel_orders.getStatus();
        String statusDesc = null;
        Email email = emailDao.findById(emailId).get();

        String emailTypeDesc = null;

        switch (email.getEmailType()) {
            case "0" : {emailTypeDesc = EmailTypeEnum.order.getDesc() ; break;}
            case "1" : {emailTypeDesc = EmailTypeEnum.arrange.getDesc() ; break;}
            case "2" : {emailTypeDesc = EmailTypeEnum.relet.getDesc() ; break;}
            case "4" : {emailTypeDesc = EmailTypeEnum.checkOutSuccess.getDesc() ; break;}

        };

        if (OrderStatusEnum.arrangeing.getCode().equals(status))  {
            statusDesc = OrderStatusEnum.arrangeing.getDesc();
        } else if (OrderStatusEnum.staying.getCode().equals(status)) {
            statusDesc = OrderStatusEnum.staying.getDesc();
        }
        hashMap.put("statusDesc" , emailTypeDesc);
        return new Result(200 , hashMap);
    }

    @Override
    public Result relet(String orderId, HashMap<String , String>hashMap , HttpSession session) {
        User userFromSession = (User)session.getAttribute("user");
        if (userFromSession == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        Hotel_orders hotel_orders = foreHotelOrderDao.findById(orderId).get();
        if (hotel_orders.getTailOrderId() != null) {
            return new Result(205,"请在最新的续租订单上续租");
        }
        SimpleDateFormat dateFormat=new SimpleDateFormat("YYYY-MM-dd");
        int compareEnd = dateFormat.format(new Date()).compareTo(hotel_orders.getEndTime().substring(0 , 10));
        if (compareEnd > 0) {
            return new Result(202 , "不好意思，已超过续租时间段");
        }
        String dayNum = hashMap.get("dayNum");
        long dayNumToLong = Long.parseLong(dayNum);
        Date currdate =DateTimeUtil.strToDate(hotel_orders.getEndTime() , "yyyy-MM-dd");
        long time = currdate.getTime(); // 得到指定日期的毫秒数
        dayNumToLong = dayNumToLong*24*60*60*1000; // 要加上的天数转换成毫秒数
        time+=dayNumToLong; // 相加得到新的毫秒数
        Date newEndDate = new Date(time);
        Hotel hotel = hotelDao.findById(hotel_orders.getScenicId()).get();
        Hotel_orders newHotel_orders  = new Hotel_orders()
                .setId(idWorker.nextId() + "")
                .setDelFlag("0")
                .setEndTime(dateFormat.format(newEndDate))
                .setStatus(OrderStatusEnum.reletNoPay.getCode())
                .setPhone(userFromSession.getMobile())
                .setUserId(userFromSession.getId())
                .setQty(1)
                .setScenicId(hotel_orders.getScenicId())
                .setScenicName(hotel_orders.getScenicName())
                .setStartTime(hotel_orders.getEndTime())
                .setUserName(userFromSession.getName())
                .setPayment(BigDecimalUtil.mul(Double.valueOf(dayNum) , hotel.getPrice()))
                .setCreateTime(new Date())
                .setModifyTime(new Date());
        if (hotel_orders.getHeadOrderId() ==null ) {
            newHotel_orders.setHeadOrderId(orderId);
            hotel_orders.setHeadOrderId(orderId);
            hotel_orders.setTailOrderId(newHotel_orders.getId());
            foreHotelOrderDao.updateHeadAndTailByEntity(hotel_orders);
        } else if (hotel_orders.getTailOrderId() == null) {
            newHotel_orders.setHeadOrderId(hotel_orders.getHeadOrderId());
            hotel_orders.setTailOrderId(newHotel_orders.getId());
            foreHotelOrderDao.updateHeadAndTailByEntity(hotel_orders);
        } else {
            return new Result(204 , "双向链表思想出问题啦！！");
        }
        //发邮件
        Email email = new Email()
                .setId(idWorker.nextId() + "")
                .setOrderId(hotel_orders.getId())
                .setSendOutUserId("")
                .setSendOutName("系统回复")
                .setReceiveUserId(hotel_orders.getUserId())
                .setReceiveName(hotel_orders.getUserName())
                .setSendTime(new Date())
                .setIsRead("0")
                .setDelFlag("0")
                .setEmailType(EmailTypeEnum.relet.getCode())
                .setOrderType(OrderTypeEnum.hotelOrder.getCode());
        emailDao.save(email);
        try{
            foreHotelOrderDao.save(newHotel_orders);
        }catch (Exception e) {
            return new Result(203 , "续租失败");
        }

        return new Result(200 , "续租成功");
    }
}
